/*
Copyright 2011 Raymond Giorgi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */

package edu.pitt.cs3530.frontend;

import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import edu.pitt.cs3530.applicationcontrolling.ServerController;
import edu.pitt.cs3530.applicationcontrolling.ServerController.Scale;
import edu.pitt.cs3530.configuration.FrontEndConfiguration;
import edu.pitt.cs3530.controllers.ReactorListener;
import edu.pitt.cs3530.reactors.ReactorQos;
import edu.pitt.cs3530.reactors.ReactorThread;
import edu.pitt.cs3530.threads.ReceiverThread.ReceiveConnection;

/**
 * A front end class that only implements the reactor
 */
public class FrontEndQosReactor extends FrontEnd implements ReactorListener {

	private long timeTurnedOn;
	private boolean turningOn;
	private long timeToChangeScale;
	private int scaleDownCounter;

	public FrontEndQosReactor() {
		super();
		String tempDirectory = FrontEndConfiguration.getInstance()
				.getTemporaryDirectory();
		long lateDeadline = FrontEndConfiguration.getInstance()
				.getLateDeadline();
		long earlyDeadline = FrontEndConfiguration.getInstance()
				.getEarlyDeadling();
		long sleepTime = FrontEndConfiguration.getInstance()
				.getReactorSleepTime();
		long oldTime = FrontEndConfiguration.getInstance()
				.getTimeToKeepRequests();
		DeleteOldFilesThread doft = new DeleteOldFilesThread(tempDirectory,
				sleepTime, oldTime);
		doft.start();
		response = new ReactorQos(lateDeadline, earlyDeadline, sleepTime, oldTime, "SimpleQoS", null);
		response.addListener(this);
		response.start();
		this.timeToChangeScale = System.currentTimeMillis();
	}

	public static void main(String args[]) {
		try {
			buildConfiguration(args[0]);
			new ReceiveConnection().start();
			new FrontEndQosReactor();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public synchronized void alertResponseListener(ReactorThread rt) {
		ServerController farm = ServerController.getInstance();
		FrontEndConfiguration fec = FrontEndConfiguration.getInstance();
		long currentTime = System.currentTimeMillis();
		if (currentTime - this.timeTurnedOn > fec.getTimeToOn()) {
			this.turningOn = false;
		}
		ReactorQos rq = (ReactorQos) rt;
		double scaleFactor = (rq.getQos() / ((fec
				.getPredictorUpperUtilization() + fec
				.getPredictorLowerScaleUtilization()) / 2)) / 3.0;
		System.out.println("!!!" + rq.getQos());
		System.out.println(timeToChangeScale - currentTime);
		if (this.timeToChangeScale < currentTime) {
			double targetFrequency = farm.getTotalFreq();
			Scale scale = Scale.NONE;
			if (rq.getQos() > fec.getPredictorUpperUtilization()) {
				targetFrequency += scaleFactor * farm.getTotalFreq();
				try {
					scale = farm.scaleUpToFrequency(targetFrequency, false);
					if (scale == Scale.NONE) {
						scale = farm.scaleUpToFrequency(targetFrequency, true);
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else if (rq.getQos() < fec.getPredictorLowerScaleUtilization()) {
				targetFrequency -= scaleFactor * farm.getTotalFreq();
				if (rq.getQos() < fec.getPredictorLowerOffUtilization()) {
					scaleFactor /= 6.0;
				} else {
					scaleFactor /= 12.0;
				}
				targetFrequency -= scaleFactor * scaleFactor;
				if (farm.oneCanScaleDown(false)) {
					try {
						scale = farm.scaleDownToFrequency(targetFrequency,
								false);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} else {
					try {
						scale = farm
								.scaleDownToFrequency(targetFrequency, true);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			if (scale == Scale.DOWN) {
				this.timeToChangeScale = currentTime + fec.getTimeToScale();
			} else if (scale == Scale.ON) {
				this.timeToChangeScale = currentTime + fec.getTimeToOn();
			} else if (scale == Scale.OFF) {
				this.timeToChangeScale = currentTime + fec.getTimeToOff();
			}
			if (scale != Scale.NONE) {
				this.scaleDownCounter = 0;
			}
		}
	}
}
